#include "InterOpDocumentUtils.h"
#include <codecvt>
#include <iostream>
#include "locale.h"

#include "SPAIop.h"
#include "SPAIopStringUtilities.h"
#include "SPAIopLengthUnit.h"
#include "locale.h"
#include "SPAIopMiscProperty.h"
#include "SPAIopMiscUserProperties.h"
#include "SPAIopAcisError.h"
#include "SPAIopMiscPropertyValueType.h"
#include "SPAIopMiscPropertyIter.h"
#include "SPAIopMiscUserPropertiesIter.h"
#include "SPAIopAcisPartImporter.h"
#include "SPAIopMiscData.h"
#include <lists.hxx>
#include <set>

using json = nlohmann::json;
using namespace SPAIopSampleStringUtils;

namespace AMCAX::InterOpDocumentUtils {


typedef std::set< unsigned int > ProcessedPartIdList;

typedef std::set< unsigned int > ProcessedAsmRefIdList;
typedef std::set< unsigned int > ImportedPartIdList;

const int SPAIopSampleResult_Import_Ok = 0;

static ProcessedPartIdList alreadyProcessedPartIdList;
static ProcessedAsmRefIdList alreadyProcessedAsmRefIdList;

static void PrintMessage(const wchar_t* iFormat, ...)
{

}

static std::string wchar_to_string(const wchar_t* wstr) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
    return converter.to_bytes(wstr);
}

static std::string SPAIopWStringToString(SPAIopWString wstr) {
	const wchar_t* pRefName = (wchar_t*)NULL;
	wstr.ToWChar(pRefName);
	return wchar_to_string(pRefName);
}


const wchar_t* GetDocumentTypeString( SPAIopDocumentType iDocType )
{
	const wchar_t* pDocTypeString = NULL;
	if( SPAIopDocumentType_Unknown == iDocType )
	{
		pDocTypeString = L"Unknown";
	}
	else if( SPAIopDocumentType_Part == iDocType )
	{
		pDocTypeString = L"Part" ;
	}
	else if( SPAIopDocumentType_Assembly == iDocType )
	{
		pDocTypeString = L"Assembly";
	}
	else if( SPAIopDocumentType_PartAndAssembly == iDocType  )
	{
		pDocTypeString = L"Part+Assembly";
	}

	return pDocTypeString;
}



void DumpJsonValue(json& oJson, const wchar_t* label, const wchar_t* value)
{
	try
	{
		std::string key_str = wchar_to_string(label);
		std::string value_str = wchar_to_string(value);

		oJson[key_str] = value_str;
	}
	catch(...)
	{

	}
}

//-----------------------------------------------------------------------------------------
void SPADumpInfo(json& oJson, const wchar_t* label, bool hasData, SPAIopWString& data) 
{
	const wchar_t* pDataArray = (wchar_t*)NULL;
	data.ToWChar(pDataArray);
	const wchar_t* pData = hasData ? pDataArray : L"[Not Set]";

	DumpJsonValue(oJson, label, pData);
}

//-----------------------------------------------------------------------------------------

void SPADumpInfo(json& oJson, const wchar_t* label, bool hasData, long double data, bool useExponential=true)
{
	if (hasData)
	{
		DumpJsonValue(oJson, label, std::to_wstring(data).c_str());
	}		
	else
	{
		const wchar_t* pData = L"[Not Set]";
		DumpJsonValue(oJson, label, pData);
	}
}

//-----------------------------------------------------------------------------------------

void SPADumpInfo(json& oJson, const wchar_t* label, bool hasData, const SPAIopLengthUnitType iUnits)
{
	SPAIopWString unitString;
	if (hasData)
		unitString = SPAIopLengthUnitUtils::LengthUnitToString(iUnits);

	SPADumpInfo(oJson, label, hasData, unitString);
}
//-----------------------------------------------------------------------------------------

void SPADumpConfigInfo(json& oJson, const wchar_t* label, bool hasData, SPAIopWString& data)
{
	const wchar_t* pDataArray = (wchar_t*)NULL;
	data.ToWChar(pDataArray);
	const wchar_t* pData = hasData ? pDataArray : L"[Not Set]";
	DumpJsonValue(oJson, label, pData);
}

void AccessInformation(SPAIopWString& iName, const SPAIopDocumentInformation& iDocInfo, bool iProductDocument, json& oJson)
{
	char oldNumericLocale[85] = { '\0' };
	const char* currentNumericLocale = setlocale(LC_NUMERIC, NULL);
	strcpy(oldNumericLocale, currentNumericLocale);
	setlocale(LC_NUMERIC, "C");

	SPAIopWString oValue;
	bool hasData = false;
	const wchar_t* pInstanceType;
	if(iProductDocument)
		pInstanceType = L"Product";
	else
		pInstanceType = L"Reference";

	PrintMessage(L"============================================================================== Information Access Start for %ls Document == \n", pInstanceType);

	const wchar_t* pNameArray = NULL;
	iName.ToWChar(pNameArray);
	PrintMessage(L".................................. Accessing Information for %ls \n", pNameArray);
	hasData = iDocInfo.GetModificationDate(oValue);
	SPADumpInfo(oJson, L"Modification Date" , hasData, oValue);

	hasData = iDocInfo.GetFileName(oValue);
	SPADumpInfo(oJson, L"File Name" , hasData, oValue);

	hasData = iDocInfo.GetLastSavedPath(oValue);
	SPADumpInfo(oJson, L"Last Saved Path", hasData, oValue);	

	hasData = iDocInfo.GetAuthor(oValue);
	SPADumpInfo(oJson, L"Author" , hasData, oValue);

	hasData = iDocInfo.GetOrganization(oValue);
	SPADumpInfo(oJson, L"Organization" , hasData, oValue);

	hasData = iDocInfo.GetSourceSystem(oValue);
	SPADumpInfo(oJson, L"Source System" , hasData, oValue);

	SPAIopWString vesrionWString(L"");
	bool isVersionSupported(false);
	isVersionSupported = iDocInfo.GetVersion(vesrionWString);
	const wchar_t* versionWcharString=nullptr;
	vesrionWString.ToWChar(versionWcharString);
	size_t versionStringSize = wcslen(versionWcharString);
	hasData = (versionStringSize > 0) ? true : false;
	SPADumpInfo(oJson, L"Version" , hasData, vesrionWString);

	SPAIopLengthUnitType units = SPAIopLengthUnit_Unknown;
	hasData = iDocInfo.GetUnits(units);
	SPADumpInfo(oJson, L"Units" , hasData, units);

	hasData = iDocInfo.GetComment(oValue);
	SPADumpInfo(oJson, L"Comment" , hasData, oValue);

	double oTol = 0.0;
	hasData = iDocInfo.GetTolerance(oTol);
	SPADumpInfo(oJson, L"Tolerance" , hasData, oTol);

	hasData = iDocInfo.GetFormatType(oValue);
	SPADumpInfo(oJson, L"Format Type" , hasData, oValue);

	hasData = iDocInfo.IsUnitless(oValue);
	if (true == hasData)
	{
		SPADumpInfo(oJson, L"Unitless", hasData, oValue);
	}

	SPAIopDocumentType docType = SPAIopDocumentType_Unknown;
	hasData = iDocInfo.GetType( docType );
	const wchar_t* pDocType = GetDocumentTypeString( docType );
	oValue = SPAIopWString( pDocType );
	SPADumpInfo(oJson, L"Document Type" , hasData, oValue);

	SPAIopVisuLODIter Iter = iDocInfo.GetVisuLODIterator();
	if(Iter.GetCount() > 0)
	{
		std::wstring wstrCount = std::to_wstring(Iter.GetCount());
		SPAIopWString strLODCount(wstrCount.c_str());
		SPADumpInfo(oJson, L"Total LODs", hasData, strLODCount);

		while(Iter.Next())
		{
			SPAIopVisuLOD currentLOD = Iter.Current();
			bool flag = false;
			double oAngularValue = 0.0f;
			double oChordalDeviationValue = 0.0f;
			double oFacetSizeValue = 0.0f;
			SPAIopWString strCurrentLODId;
			flag = currentLOD.GetID(strCurrentLODId);
		
			int value_LOD_ID(0);
			const wchar_t* idValueLODString = NULL;
			strCurrentLODId.ToWChar(idValueLODString);
			int bytesWritten = swscanf(idValueLODString, L"%d", &value_LOD_ID);
		
			if (flag && bytesWritten > 0)
			{
				wchar_t currentLODLabel[20] = { '\0' };
				swprintf(currentLODLabel, sizeof currentLODLabel / sizeof *currentLODLabel, L"LOD%d", value_LOD_ID);

				SPADumpInfo(oJson, currentLODLabel, flag, strCurrentLODId);
			}
			flag = currentLOD.GetMaxAngularDeviation(oAngularValue);
			if (flag && (oAngularValue > -1.0))
			{
				wchar_t currentAngular[22] = { '\0' };
				swprintf(currentAngular, sizeof currentAngular / sizeof *currentAngular, L"AngularDeviation%d", value_LOD_ID);
				SPADumpInfo(oJson, currentAngular, flag, oAngularValue, false); //'false' flag is used for floating format instead of exponential.
			}
			flag = currentLOD.GetMaxChordalDeviation(oChordalDeviationValue);
			if (flag && (oChordalDeviationValue > -1.0))
			{
				wchar_t currentChordal[22] = { '\0' };
				swprintf(currentChordal, sizeof currentChordal / sizeof *currentChordal, L"ChordalDeviation%d", value_LOD_ID);
				SPADumpInfo(oJson, currentChordal, flag, oChordalDeviationValue, false);//'false' flag is used for floating format instead of exponential.
			}
			flag = currentLOD.GetMaxTessellationSize(oFacetSizeValue);
			if (flag && (oFacetSizeValue > -1.0))
			{
				wchar_t currentSegLength[22] = { '\0' };
				swprintf(currentSegLength, sizeof currentSegLength / sizeof *currentSegLength, L"MaxTessallationSize%d", value_LOD_ID);
				SPADumpInfo(oJson, currentSegLength, flag, oFacetSizeValue, false);//'false' flag is used for floating format instead of exponential.
			}
		}
	}

	SPAIopConfigurationIter configIter = iDocInfo.GetConfigurationIterator();
	if (configIter.GetCount() > 0)
	{
		SPAIopWString nullStr(L"");
		SPADumpInfo(oJson, L"Configurations", true, nullStr);

		std::wstring wstrCount = std::to_wstring(configIter.GetCount());
		SPAIopWString strConfigCount(wstrCount.c_str());
		SPADumpConfigInfo(oJson, L"Configurations Count", true, strConfigCount);

		SPAIopWString activeConfig;
		hasData = iDocInfo.GetActiveConfiguration(activeConfig);

		int configLabel = 0;
		while (configIter.Next())
		{
			oValue = configIter.Current();
			configLabel++;
			wchar_t currentConfigLabel[21] = { '\0' };
			swprintf(currentConfigLabel, sizeof currentConfigLabel / sizeof *currentConfigLabel, L"Configuration %d", configLabel);

			const wchar_t* oConfigStr = NULL;
			oValue.ToWChar(oConfigStr);
			const wchar_t* oActiveConfigStr = NULL;
			activeConfig.ToWChar(oActiveConfigStr);

			if (0 == wcscmp(oConfigStr, oActiveConfigStr))
			{
				std::wstring newConfigValueStr = std::wstring(oConfigStr) + std::wstring(L", Active = true");
				SPAIopWString newConfigValue(newConfigValueStr.c_str());
				SPADumpConfigInfo(oJson, currentConfigLabel, true, newConfigValue);
			}
			else
				SPADumpConfigInfo(oJson, currentConfigLabel, true, oValue);
		}
	}
	
	if (!isVersionSupported)
		PrintMessage(L"File version is not supported \n");
		
	PrintMessage(L"============================================================================== Information Access End for %ls Document == \n", pInstanceType);

	setlocale(LC_NUMERIC, oldNumericLocale);
}
//-----------------------------------------------------------------------------------------
bool AccessDocumentInformation(const wchar_t*& ipSourceFilePath, json& oJson)
{
	bool retVal(true);
	std::wstring sourceFilePath(ipSourceFilePath);
	SPAIopWString filePath( sourceFilePath.c_str());
	
	SPAIopWString fileName;
	GetNameWithExtension(filePath, fileName);
	const wchar_t * pFileName = NULL;
	fileName.ToWChar(pFileName);
	PrintMessage(L"Reading document info for: %ls \n", pFileName);

	SPAIopPSImportResult interopResult;

	SPAIopDocument iDoc(filePath);

	SPAIopDocumentInformation pMainDocInfo = iDoc.GetInformation();
	bool productDoc = true;
	AccessInformation(fileName, pMainDocInfo, productDoc, oJson);

	SPAIopProductStructureImporter psImporter;
	SPAIopInputProductStructure inputPS;

	interopResult = psImporter.Import(iDoc, inputPS);
		
	int nbContainers = inputPS.GetUniqueReferencesCount();

	for (int i = 0; i < nbContainers; i++ ) 
	{
		SPAIopInputPSReference reference = inputPS.GetUniqueReference(i);
		SPAIopPartDocument sourceDoc = reference.GetDocument();
		SPAIopWString refName =  reference.GetReferenceName();
		SPAIopWString storageName =  reference.GetStorageName();

		const wchar_t* pRefName = (wchar_t*)NULL;
		refName.ToWChar(pRefName);
							
		SPAIopDocumentInformation partInfo = sourceDoc.GetInformation();

		json childJson;
		bool productDoc = false;
		AccessInformation(refName, partInfo, productDoc, childJson);

		std::string strRefName = wchar_to_string(pRefName);
		oJson[strRefName] = childJson;
	}

	return retVal;

}

//-----------------------------------------------------------------------------------------

bool ImportProductStructure(SPAIopDocument& ipSourceDoc, const SPAIopPolicy& iPolicy, SPAIopInputProductStructure& oInputPS)
{
	bool returnValue = true;

	try
	{
		// Create a Product Strcuture Importer
		SPAIopProductStructureImporter psImporter;
		SPAIopPSImportResult res = psImporter.ImportWithPolicy(iPolicy, ipSourceDoc, oInputPS);

	}
	catch (SPAIopAcisError& error)
	{
		returnValue = false;
	}
	catch (...)
	{
		std::wcout << "FAILURE: Unknown exception caught! " << std::endl;
		returnValue = false;
	}

	return returnValue;
}

static void PropertyTypeToString( SPAIopMiscPropertyType iPropertyType, SPAIopWString& oPropertyType );


///
static void ProcessUserProperties( SPAIopMiscUserProperties& iUserProperties, nlohmann::json& outJson);
static int ProcessInputProductStructure(const SPAIopInputProductStructure& iInputPS, const SPAIopPolicy& iPolicy, nlohmann::json& outJson);
static int ProcessReference(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy, nlohmann::json& outJson);
static void ProcessInstance(const SPAIopInputPSInstance& iInputPSInstance, const SPAIopPolicy& iPolicy, nlohmann::json& outJson);
static void ProcessAsmReference(const SPAIopInputPSReference& iInputPSRefrence, nlohmann::json& outJson);
static void TraversePSInstanceMiscData( SPAIopPSInstanceMiscData& iPSInstanceMiscData, nlohmann::json& outJson);
static void ProcessPart(SPAIopPartDocument& partDocument, const SPAIopPolicy& iPolicy, nlohmann::json& outJson);

void ProcessUserProperties( SPAIopMiscUserProperties& iUserProperties, nlohmann::json& outJson)
{
	SPAIopWString userPropertiesName = iUserProperties.GetName();
	
	//PrintMessage( L" User Properties Name: %ls\n", (const wchar_t*)userPropertiesName );
	const wchar_t *pUserPropertiesNameArray  = NULL;
	userPropertiesName.ToWChar(pUserPropertiesNameArray);

	outJson = json::array();

	SPAIopWString strBuffer;
	int count = 0;
	SPAIopMiscPropertyIter propertyIter=iUserProperties.GetPropertyIterator();
	while( propertyIter.Next() )
	{
		SPAIopMiscProperty userProperty=propertyIter.Current();

		SPAIopWString strParmType;
		PropertyTypeToString(userProperty.GetType(), strParmType);
		//size_t buffersize = wcslen( ( const wchar_t*)strBuffer);
		const wchar_t *pStrBufferArray = NULL;
		strBuffer.ToWChar(pStrBufferArray);
		size_t buffersize = wcslen(pStrBufferArray);
		//buffersize = buffersize + 516;
		//wchar_t* pTmpbuffer = new wchar_t[buffersize];
		//swprintf( pTmpbuffer, buffersize, L"%ls\t Property#%d Name: %ls, Value: %ls, Type: %ls \n", ( const wchar_t*)strBuffer, count, ( const wchar_t*)userProperty.Name, ( const wchar_t*)userProperty.Value, ( const wchar_t*)strParmType);	
		SPAIopWString userPropertyName = userProperty.GetName();
		const wchar_t *pUserPropertyNameArray = NULL;
		userPropertyName.ToWChar(pUserPropertyNameArray);
		SPAIopWString userPropertyValue = userProperty.GetValue();
		const wchar_t *pUserPropertyValueArray = NULL;
		userPropertyValue.ToWChar(pUserPropertyValueArray );
		const wchar_t *pStrParmTypeArray = NULL;
		strParmType.ToWChar(pStrParmTypeArray);

		nlohmann::json propertyObject;
		propertyObject["Name"] = wchar_to_string(pUserPropertyNameArray);
		propertyObject["Type"] = wchar_to_string(pStrParmTypeArray);
		propertyObject["Value"] = wchar_to_string(pUserPropertyValueArray);
		
		outJson.push_back(propertyObject);
		
		// size_t totalArraySize = wcslen(pUserPropertyNameArray) + wcslen(pUserPropertyValueArray) + wcslen(pStrParmTypeArray);
		// buffersize += (totalArraySize + 38 + 2 + 516);
		// wchar_t* pTmpbuffer = new wchar_t[buffersize];
		// swprintf( pTmpbuffer, buffersize, L"%ls\t Property#%d Name: %ls, Value: %ls, Type: %ls \n",pStrBufferArray, count, pUserPropertyNameArray, pUserPropertyValueArray ,pStrParmTypeArray);
		// strBuffer = (SPAIopWString)pTmpbuffer;
		// if( pTmpbuffer)
		// {
		// 	delete[] pTmpbuffer;
		// 	pTmpbuffer = NULL;
		// }
		count++;
	}
	PrintMessage( L" Property Count: %d \n", count);
	//PrintMessage( L"%ls", ( const wchar_t*)strBuffer);
	const wchar_t *pStrBufferArray = NULL;
	strBuffer.ToWChar(pStrBufferArray);
	PrintMessage( L"%ls",pStrBufferArray);
}

void AccessProperties(SPAIopWString wSourceFilePath, nlohmann::json& oJson, bool includingChildren)
{
	SPAIopPolicy iPolicy;
	SPAIopInputProductStructure inputPS;
	SPAIopDocument sourceDoc(wSourceFilePath);
	ImportProductStructure(sourceDoc, iPolicy, inputPS);

	// access document information
	SPAIopDocumentInformation docInfo = sourceDoc.GetInformation();
	SPAIopDocumentType docType = SPAIopDocumentType_Unknown;
	docInfo.GetType(docType);

	if (docType == SPAIopDocumentType::SPAIopDocumentType_Part)
	{
		SPAIopInputPSInstance inputPartInstance = inputPS.GetRootInstance(0);
		SPAIopInputPSReference inputPartRef = inputPartInstance.GetReference();
		SPAIopPartDocument partReferneceDoc = inputPartRef.GetDocument();

		ProcessPart(partReferneceDoc, iPolicy, oJson);
	}
	else
	{
		SPAIopPSReferenceMiscData assemblyRootMiscData = inputPS.GetMiscData();

		int userPropertiesIndex = 0;
		SPAIopMiscUserPropertiesIter userPropertiesIter = assemblyRootMiscData.GetUserPropertiesIterator();
		while( userPropertiesIter.Next())
		{
			PrintMessage( L"\n ========== Begin Processing User Properties: %d ========== \n\n", userPropertiesIndex );

			nlohmann::json properties;
			SPAIopMiscUserProperties userProperties = userPropertiesIter.Current();
			ProcessUserProperties( userProperties, properties);
			
			SPAIopWString name =  userProperties.GetName();
			const wchar_t* pRefName = (wchar_t*)NULL;
			name.ToWChar(pRefName);
			oJson[wchar_to_string(pRefName)] = properties;

			PrintMessage( L"\n ========== End Processing User Properties: %d ========== \n\n", userPropertiesIndex );

			++userPropertiesIndex;
		}

		if (includingChildren)
		{
			nlohmann::json instanceProperties;
			ProcessInputProductStructure(inputPS, iPolicy, instanceProperties);
			oJson["Instances"] = instanceProperties;
		}
		
	}

}

void ProcessPart(SPAIopPartDocument& partReferneceDoc, const SPAIopPolicy& iPolicy, nlohmann::json& oJson)
{
	SPAIopAcisPartImporter partImporter;
	ENTITY_LIST acisEntities;
	SPAIopPartImportResult partImportResult = partImporter.ImportWithPolicy(iPolicy, partReferneceDoc, acisEntities);

	SPAIopMiscData miscData = partImportResult.GetMiscData();
	//SPAIopMiscUtils::AccessMiscData(miscData);

	int userPropertiesIndex = 0;
	SPAIopMiscUserPropertiesIter userPropertiesIter = miscData.GetUserPropertiesIterator();
	while( userPropertiesIter.Next())
	{
		PrintMessage( L"\n ========== Begin Processing User Properties: %d ========== \n\n", userPropertiesIndex );

		nlohmann::json properties;
		SPAIopMiscUserProperties userProperties = userPropertiesIter.Current();
		ProcessUserProperties( userProperties, properties);
		
		SPAIopWString name =  userProperties.GetName();
		auto strName = SPAIopWStringToString(name);
		oJson[strName] = properties;

		PrintMessage( L"\n ========== End Processing User Properties: %d ========== \n\n", userPropertiesIndex );

		++userPropertiesIndex;
	}
}

void TraversePSInstanceMiscData( SPAIopPSInstanceMiscData& iPSInstanceMiscData,  nlohmann::json& outObject)
{
	// Retrieve the iterator for the Instance Miscellaneous data and traverse
	// it to get the different data.

	SPAIopWString congigurationName = iPSInstanceMiscData.GetConfigurationName();
	const wchar_t* pConfigurationNameArray = NULL;
	congigurationName.ToWChar( pConfigurationNameArray );
	PrintMessage( L" Configuration Name: %ls \n", pConfigurationNameArray );
	
	int userPropertiesIndex = 0;
	SPAIopMiscUserPropertiesIter userPropertiesIter = iPSInstanceMiscData.GetUserPropertiesIterator();
	while( userPropertiesIter.Next())
	{
		PrintMessage( L"\n ========== Begin Processing User Properties: %d ========== \n\n", userPropertiesIndex );

		SPAIopMiscUserProperties userProperties = userPropertiesIter.Current();

		ProcessUserProperties( userProperties, outObject);

		PrintMessage( L"\n ========== End Processing User Properties: %d ========== \n\n", userPropertiesIndex );

		++userPropertiesIndex;
	}	
}

//-----------------------------------------------------------------------------------------

int ProcessInputProductStructure(const SPAIopInputProductStructure& iInputPS, const SPAIopPolicy& iPolicy, nlohmann::json& instanceProperties)
{
	alreadyProcessedPartIdList.clear();
	alreadyProcessedAsmRefIdList.clear();
	int result = SPAIopSampleResult_Import_Ok;
	int numInstances = iInputPS.GetRootInstancesCount();
	int i = 0;
	instanceProperties = nlohmann::json::array();
	for (i = 0; i < numInstances; i++)
	{
		// Get Input PS info (instance, reference, ID)
		SPAIopInputPSInstance inputRootInstance = iInputPS.GetRootInstance(i);
		
		nlohmann::json instanceProperty;

		ProcessInstance(inputRootInstance, iPolicy, instanceProperty);

		instanceProperties.push_back(instanceProperty);
	}

	alreadyProcessedPartIdList.clear();
	alreadyProcessedAsmRefIdList.clear();
	return result;
}

//-----------------------------------------------------------------------------------------

void ProcessInstance(const SPAIopInputPSInstance& iInputPSInstance, const SPAIopPolicy& iPolicy, nlohmann::json& instanceProperty)
{
	SPAIopPSInstanceMiscData instanceMiscData = iInputPSInstance.GetMiscData();
	//SPAIopMiscUtils::AccessPSInstanceMiscData(instanceMiscData);

	if (true == instanceMiscData.HasData())
	{
		setlocale( LC_NUMERIC, "C" ); // ensure consistent data output string formatting
		TraversePSInstanceMiscData( instanceMiscData, instanceProperty);
	}
	else
	{
		PrintMessage( L"\n ========== The Product Structure Instance does not contain Miscellaneous Data ========== \n" );
	}

	nlohmann::json referenceObject;
	SPAIopInputPSReference inputReference = iInputPSInstance.GetReference();
	ProcessReference(inputReference, iPolicy, referenceObject);


	instanceProperty["Reference"] =  referenceObject;
}

//-----------------------------------------------------------------------------------------

bool IsPartReferenceAlreadyProcessed(const SPAIopInputPSReference& iInputPSRefrence)
{
	bool partReferenceProcessed(true);

	unsigned int referenceID = iInputPSRefrence.GetUniqueID();
	ImportedPartIdList::iterator alreadyProcessedIter = alreadyProcessedPartIdList.find(referenceID);
	if (alreadyProcessedIter == alreadyProcessedPartIdList.end())
	{
		alreadyProcessedPartIdList.insert(referenceID);
		partReferenceProcessed = false;
	}

	return partReferenceProcessed;
}

//-----------------------------------------------------------------------------------------

bool IsAsmReferenceAlreadyProcessed(const SPAIopInputPSReference& iInputPSRefrence)
{
	bool asmReferenceProcessed(true);

	unsigned int referenceID = iInputPSRefrence.GetUniqueID();
	ImportedPartIdList::iterator alreadyProcessedIter = alreadyProcessedAsmRefIdList.find(referenceID);
	if (alreadyProcessedIter == alreadyProcessedAsmRefIdList.end())
	{
		alreadyProcessedAsmRefIdList.insert(referenceID);
		asmReferenceProcessed = false;
	}

	return asmReferenceProcessed;
}


int ProcessReference(const SPAIopInputPSReference& iInputPSRefrence, const SPAIopPolicy& iPolicy, nlohmann::json& oJson)
{
	int result = SPAIopSampleResult_Import_Ok;
	if (iInputPSRefrence.IsPart())
	{
		//if (false == IsPartReferenceAlreadyProcessed(iInputPSRefrence))
		{
			SPAIopPartDocument partReferneceDoc = iInputPSRefrence.GetDocument();
			ProcessPart(partReferneceDoc, iPolicy, oJson);
		}
	}
	else
	{
		//if (false == IsAsmReferenceAlreadyProcessed(iInputPSRefrence))
		// {
		// 	ProcessAsmReference(iInputPSRefrence);
			
		// 	int numInstances = iInputPSRefrence.GetInstancesCount();
		// 	int i;
		// 	for (i = 0; i < numInstances; i++)
		// 	{
		// 		SPAIopInputPSInstance currentInstance = iInputPSRefrence.GetInstance(i);
		// 		ProcessInstance(currentInstance, iPolicy);

		// 		SPAIopInputPSReference currentReference = currentInstance.GetReference();
		// 		int res = ProcessReference(currentReference, iPolicy);
		// 		if (0 != res)
		// 			result = res;
		// 	}
		// }

		SPAIopPSReferenceMiscData assemblyMiscData = iInputPSRefrence.GetMiscData();

		int userPropertiesIndex = 0;
		SPAIopMiscUserPropertiesIter userPropertiesIter = assemblyMiscData.GetUserPropertiesIterator();
		while( userPropertiesIter.Next())
		{
			PrintMessage( L"\n ========== Begin Processing User Properties: %d ========== \n\n", userPropertiesIndex );

			nlohmann::json properties;
			SPAIopMiscUserProperties userProperties = userPropertiesIter.Current();
			ProcessUserProperties( userProperties, properties);
			
			SPAIopWString name =  userProperties.GetName();
			const wchar_t* pRefName = (wchar_t*)NULL;
			name.ToWChar(pRefName);
			oJson[wchar_to_string(pRefName)] = properties;

			PrintMessage( L"\n ========== End Processing User Properties: %d ========== \n\n", userPropertiesIndex );

			++userPropertiesIndex;
		}

		nlohmann::json instanceProperties;

		int numInstances = iInputPSRefrence.GetInstancesCount();
		int i = 0;
		instanceProperties = nlohmann::json::array();
		for (i = 0; i < numInstances; i++)
		{
			// Get Input PS info (instance, reference, ID)
			SPAIopInputPSInstance inputInstance = iInputPSRefrence.GetInstance(i);
			
			nlohmann::json instanceProperty;
			ProcessInstance(inputInstance, iPolicy, instanceProperty);
			instanceProperties.push_back(instanceProperty);
		}

		oJson["Instances"] = instanceProperties;

	}
	return result;
}

//============================================================

void PropertyTypeToString( SPAIopMiscPropertyType iPropertyType, SPAIopWString& oPropertyType )
{
	switch( iPropertyType )
	{
		case SPAIopMisc_PropertyType_String:
			oPropertyType = SPAIopWString( L"PropertyType_String");
			break;
		case SPAIopMisc_PropertyType_Integer:
			oPropertyType = SPAIopWString( L"PropertyType_Integer");
			break;
		case SPAIopMisc_PropertyType_Double:
			oPropertyType = SPAIopWString( L"PropertyType_Double");
			break;
		case SPAIopMisc_PropertyType_Boolean:
			oPropertyType = SPAIopWString( L"PropertyType_Boolean");
			break;
		case SPAIopMisc_PropertyType_DateTime:
			oPropertyType = SPAIopWString( L"PropertyType_DateTime");
			break;
		default:
			oPropertyType = SPAIopWString( L"PropertyType_Unknown");
	}
}

}